package com.fight.strive.sys.modules.auth.service.impl;

import com.fight.strive.sys.modules.auth.dto.StriveAuthInfo;
import com.fight.strive.sys.modules.auth.service.StriveAuthService;
import com.fight.strive.sys.modules.cauth.service.AppClientService;
import com.fight.strive.sys.modules.exception.StriveException;
import com.fight.strive.sys.modules.exception.exceptions.StriveInvalidTokenException;
import com.fight.strive.sys.modules.rbac.entity.RbacUserEntity;
import com.fight.strive.sys.modules.rbac.service.RbacUserService;
import com.fight.strive.sys.modules.redis.RedisUtils;
import com.fight.strive.sys.modules.validator.StriveValid;
import com.fight.strive.sys.utils.DigestUtils;
import com.fight.strive.sys.utils.ObjectUtils;
import com.fight.strive.sys.utils.RequestUtils;
import com.fight.strive.sys.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class StriveAuthServiceImpl implements StriveAuthService {

    private final String loginNameKey = "sys:authenticate:login_name:";

    private final String tokenKey = "sys:authenticate:token:";

    @Resource
    private RbacUserService rbacUserService;

    @Resource
    private AppClientService appClientService;

    @Override
    public StriveAuthInfo login(@StriveValid StriveAuthInfo info) {
        RbacUserEntity user = rbacUserService.getByLoginName(info.getLoginName());
        if (ObjectUtils.isNull(user) || !StringUtils.equals(
                DigestUtils.md5Hex(info.getPassword()), user.getPassword())) {
            // 密码不对登录失败
            throw new StriveException("用户名或密码错误");
        }
        String token = RedisUtils.valueGet(
                loginNameKey.concat(info.getLoginName()), String.class);
        if (ObjectUtils.isNull(token)) {
            token = UUID.randomUUID().toString();
        }
        // 设置 token 信息
        info.setUser(user).setPassword(user.getPassword())
                .setToken(token);
        // 保存登录信息
        this.updateCache(info);
        return info;
    }

    @Override
    public void logout() {
        StriveAuthInfo info = this.getCurrentAuth();
        if (ObjectUtils.notNull(info)) {
            this.removeCache(info);
        }
    }

    @Override
    public void logoutByLoginName(String loginName) {
        RbacUserEntity user =
                rbacUserService.getByLoginName(loginName);
        if (ObjectUtils.notNull(user)) {
            String token = RedisUtils.valueGet(
                    loginNameKey.concat(loginName), String.class);
            if (ObjectUtils.notNull(token)) {
                StriveAuthInfo info = this.getAuthByToken(token);
                this.removeCache(info);
            }
        }
    }

    @Override
    public StriveAuthInfo getCurrentAuth() {
        StriveAuthInfo dto = null;
        HttpServletRequest request = RequestUtils.getServletRequest();
        if (ObjectUtils.isNull(request)) {
            // 表示非请求执行
            return null;
        }
        String token = request.getHeader("token");
        String appKey = request.getHeader("appKey");
        if (ObjectUtils.notNull(token)) {
            if (StringUtils.isNotBlank(appKey)) {
                // 应用认证信息
                dto = appClientService.getAuthByAppKey(appKey);
            } else {
                // 人员认证信息
                dto = this.getAuthByToken(token);
            }
        }
        if (ObjectUtils.isNull(dto)) {
            // 没有获取到当前用户，表示未登录
            throw new StriveInvalidTokenException();
        }
        return dto;
    }

    @Override
    public StriveAuthInfo getAuthByToken(String token) {
        return RedisUtils.valueGet(
                tokenKey.concat(token), StriveAuthInfo.class);
    }

    @Override
    public StriveAuthInfo updateAuthInfo() {
        StriveAuthInfo info = this.getCurrentAuth();
        RbacUserEntity user = rbacUserService.getByLoginName(info.getLoginName());
        this.updateCache(info);
        return info;
    }

    @Override
    public StriveAuthInfo updateAuthInfoByLoginName(String loginName) {
        String token = RedisUtils.valueGet(
                loginNameKey.concat(loginName), String.class);
        if (ObjectUtils.notNull(token)) {
            StriveAuthInfo info = this.getAuthByToken(token);
            this.updateCache(info);
            return info;
        }
        return null;
    }

    @Override
    public void updateCache(StriveAuthInfo info) {
        if (ObjectUtils.notNull(info)) {
            RedisUtils.valueSet(tokenKey.concat(info.getToken()),
                    info, 30, TimeUnit.DAYS);
            RedisUtils.valueSet(loginNameKey.concat(info.getLoginName()),
                    info.getToken(), 30, TimeUnit.DAYS);
        }
    }

    @Override
    public void removeCache(StriveAuthInfo info) {
        if (ObjectUtils.notNull(info)) {
            RedisUtils.valueDelete(tokenKey.concat(info.getToken()));
            RedisUtils.valueDelete(loginNameKey.concat(info.getLoginName()));
        }
    }
}
